﻿using System;
using System.Windows;
using System.Windows.Controls;
using AzukiMap.Map;
using AzukiMap.Tools;
using AzukiMap.Tools.Logger;
using Geotools.Geometries;
using AzukiMap.MapSource;
using System.Xml.Linq;
using System.Windows.Input;
using System.Collections.Generic;
using System.Windows.Media.Imaging;

namespace AzukiMap.Layer
{
    public abstract class Layer : ILayer
    {
        protected MapSourceTypes _type;
        protected string _name;
        protected bool _readable;
        protected bool _writable;
        protected bool _isRendering;
        protected GTPolygon _maxExtent;
        protected double _maxScale;
        protected double _minScale;
        protected MapCanvas _mapCanvas;
        protected GeometryUtil _geoUtil;
        protected IMapSource _mapSource;
        protected bool _isVisible;
        protected double _opacity;
        protected BitmapImage _dataTypeIcon;

        string _iconSrc;
        XmlCommon _xmlCommon;
        protected MouseButtonEventHandler _onElementMouseLeftDown;
        protected MouseButtonEventHandler _onElementMouseLeftUp;
        protected MouseEventHandler _onElementMouseEnter;
        protected MouseEventHandler _onElementMouseLeave;

        public string Name
        {
            set { _name = value; }
            get { return _name; }
        }

        public MapSourceTypes Type
        {
            protected set { _type = value; }
            get { return _type; }
        }

        public Visibility Visiblity
        {
            get { return GetVisibility(); }
            set { SetVisibility(value); }
        }

        public bool Readable
        {
            get { return _readable; }
            set { _readable = value; }
        }

        public bool Writable
        {
            get { return _writable; }
            set { _writable = value; }
        }

        public GTPolygon MaxExtent
        {
            get { return _maxExtent; }
            set { 
                _maxExtent = value; 
            }
        }

        public bool IsRendering
        {
            get { return _isRendering; }
        }

        public int ZIndex
        {
            get { return GetZIndex(); }
            set { SetZIndex(value); }
        }

        public double MaxScale
        {
            get { return _maxScale; }
            set {
                if (value <= 0)
                {
                    Logger.Error("Layer.MaxScale: MaxScale should be > 0.");
                    throw new ArgumentException("MaxScale should be > 0.", "MaxScale");
                }
                if ( _minScale != 0 && _minScale > value) {
                    Logger.Error("Layer.MaxScale: MaxScale should be greater than MinScale.");
                    throw new ArgumentException("MaxScale should be greater than MinScale.", "MinScale");
                }
                _maxScale = value;
            }
        }

        public double MinScale
        {
            get
            {
                return _minScale;
            }
            set
            {
                if (value < 0)
                {
                    Logger.Error("Layer.minScale: MinScale should be >= 0.");
                    throw new ArgumentException("MinScale should be >= 0.", "MinScale");
                }
                if ( _maxScale != 0 && _maxScale < value) {
                    Logger.Error("Layer.minScale: MinScale should be less than MinScale.");
                    throw new ArgumentException("MinScale should be less than MinScale.", "MinScale");
                }
                _minScale = value;
            }
        }

        public double Opacity
        {
            get { return _opacity; }
            set {
                if (value < 0)
                    _opacity = 0;
                if (value > 1)
                    _opacity = 1;
                if ( value >=0 && value <= 1)
                    _opacity = value;
                _mapCanvas.Opacity = _opacity;
            }
        }

        public BitmapImage DataTypeIcon
        {
            get { return _dataTypeIcon; }
            set { _dataTypeIcon = value; }
        }

        public MapCanvas MapCanvas
        {
            get { return _mapCanvas; }
        }

        public bool IsVisible
        {
            get
            {
                return _isVisible; 
            }
        }

        public string IconSrc
        {
            set { _iconSrc = value; }
            get { return _iconSrc; }
        }

        public MouseButtonEventHandler OnElementMouseLeftDown
        {
            get { return _onElementMouseLeftDown; }
            set { _onElementMouseLeftDown = value; }
        }

        public MouseButtonEventHandler OnElementMouseLeftUp
        {
            get { return _onElementMouseLeftUp; }
            set { _onElementMouseLeftUp = value; }
        }

        public MouseEventHandler OnElementMouseEnter
        {
            get { return _onElementMouseEnter; }
            set { _onElementMouseEnter = value; }
        }

        public MouseEventHandler OnElementMouseLeave
        {
            get { return _onElementMouseLeave; }
            set { _onElementMouseLeave = value; }
        }

        protected Layer(MapCanvas mapCanvas, MapSourceTypes dataType, IMapSource mapSource)
        {
            _mapCanvas = mapCanvas;
            _type = dataType;
            _name = GetDefaultName();
            _readable = true;
            _writable = true;
            _isRendering = false;
            _maxExtent = null;
            _minScale = AzukiMapSettings.DEFAULT_MINSCALE;
            _maxScale = AzukiMapSettings.DEFAULT_MAXSCALE;
            _geoUtil = new GeometryUtil();
            _xmlCommon = new XmlCommon();
            _mapSource = mapSource;
            _isVisible = true;
            _opacity = 1;
            SetZIndex(0);
        }

        string GetDefaultName()
        {
            return Type.ToString() + "_" + Serial.GetSerialNumber();
        }

        int GetZIndex()
        {
            return (int)_mapCanvas.GetValue(Canvas.ZIndexProperty);
        }

        void SetZIndex(int zIndex)
        {
            _mapCanvas.SetValue(Canvas.ZIndexProperty, zIndex);
        }

        Visibility GetVisibility()
        {
            if (_mapCanvas == null)
            {
                Logger.Warn("Layer.GetVisibility(): LayerCanvas is not set yet.");
                return Visibility.Collapsed;
            }
            return (Visibility)_mapCanvas.GetValue(Canvas.VisibilityProperty);
        }

        void SetVisibility(Visibility v)
        {
            if (_mapCanvas == null)
            {
                Logger.Warn("Layer.SetVisibility(): LayerCanvas is not set yet.");
                return;
            }
            if (v == Visibility.Visible)
            {
                _isVisible = true;
                Draw();
            }
            else
            {
                _isVisible = false;
            }
            _mapCanvas.SetValue(Canvas.VisibilityProperty, v);
        }

        public virtual void Show()
        {
            if (_mapCanvas == null)
            {
                Logger.Warn("Layer.Show(): LayerCanvas is not set yet.");
                return;
            }
            _mapCanvas.SetValue(Canvas.VisibilityProperty, Visibility.Visible);
        }

        public virtual void Hide()
        {
            if (_mapCanvas == null)
            {
                Logger.Warn("Layer.Hide(): LayerCanvas is not set yet.");
                return;
            }
            _mapCanvas.SetValue(Canvas.VisibilityProperty, Visibility.Collapsed);
        }

        public virtual void Draw()
        {
        }

        public virtual void Clear()
        {
            _mapCanvas.Children.Clear();
        }

        protected bool CheckScale()
        {
            bool inRange = true;
            if (_mapCanvas.GeoScale < _minScale)
            {
                inRange = false;
            }
            if (_mapCanvas.GeoScale > _maxScale)
            {
                inRange = false;
            }
            return inRange;
        }

        protected bool CheckMaxExtent()
        {
            bool inMaxExtent = true;
            if (_maxExtent == null)
            {
                return inMaxExtent;
            }

            if (_mapCanvas.CurrentExtent.Disjoint(_maxExtent))
            {
                inMaxExtent = false;
            }
            return inMaxExtent;
        }

        protected void SetEventsOnElement(List<FrameworkElement> elems)
        {
            if (_onElementMouseEnter != null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.MouseEnter += _onElementMouseEnter;
                }
            }

            if (_onElementMouseLeave != null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.MouseLeave += _onElementMouseLeave;
                }
            }

            if (_onElementMouseLeftDown != null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.MouseLeftButtonDown += _onElementMouseLeftDown;
                }
            }

            if (_onElementMouseLeftUp != null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.MouseLeftButtonUp += _onElementMouseLeftUp;
                }
            }

            if (_onElementMouseEnter == null && 
                _onElementMouseLeave == null && 
                _onElementMouseLeftDown == null && 
                _onElementMouseLeftUp == null)
            {
                foreach (FrameworkElement elem in elems)
                {
                    elem.IsHitTestVisible = false;
                }
            }

        }


        public virtual XElement ToXml()
        {
            return ToXml("Layer");
        }

        public virtual XElement ToXml(string elementName)
        {
            XElement layerXml = new XElement(elementName,
                _xmlCommon.CreateAttr("Name", _name),
                _xmlCommon.CreateAttr("Type", _type),
                _xmlCommon.CreateAttr("Readable", _readable),
                _xmlCommon.CreateAttr("Writable", _writable),
                _xmlCommon.GeometryToXml("MaxExtent", _maxExtent),
                _xmlCommon.CreateAttr("MaxScale", _maxScale),
                _xmlCommon.CreateAttr("MinScale", _minScale),
                _xmlCommon.CreateAttr("Opacity", _opacity),
                _xmlCommon.CreateAttr("Visibie", _isVisible),
                _mapSource.ToXml()
                );
            return layerXml;
        }

        public virtual void Parse(XElement xmlSource)
        {
            _name = _xmlCommon.GetStringAttr(xmlSource, "Name");
            _type = (MapSourceTypes)_xmlCommon.GetEnumAttr(xmlSource, "Type", typeof(MapSourceTypes));
            _readable = _xmlCommon.GetBoolAttr(xmlSource, "Readable");
            _writable = _xmlCommon.GetBoolAttr(xmlSource, "Writable");
            _maxExtent = (GTPolygon)_xmlCommon.GetGeomElement(xmlSource, "MaxExtent");
            _maxScale = _xmlCommon.GetDoubleAttr(xmlSource, "MaxScale");
            _minScale = _xmlCommon.GetDoubleAttr(xmlSource, "MinScale");
            _isVisible = _xmlCommon.GetBoolAttr(xmlSource, "Visible");
            Opacity = _xmlCommon.GetDoubleAttr(xmlSource, "Opacity");
            //_mapSource.Parse(xmlSource.Element("MapSource"));
        }

        /*
        protected MapSourceTypes _layer;
        protected string _name;
        protected bool _readable;
        protected bool _writable;
        protected bool _isRendering;
        protected GTPolygon _maxExtent;
        protected double _maxScale;
        protected double _minScale;
        protected MapCanvas _mapCanvas;
        protected GeometryUtil _geoUtil;
        protected IMapSource _mapSource;
         */

    }
}
